package org.swfer.UICExtend 
{
	/**
	 * 简单过渡引擎.其中时间计算是粗略的
	 * 适用任何数字类型和布尔类型的值过渡。
	 * 参数说明：
	 * delay 延迟时间 （ms）
	 * onComplete 过渡完成回掉函数
	 * ease 缓动类型
	 * @author swfer
	 */
	public class Tween 
	{
		/** 缓和系数 **/
		public static const SineInOut:String = 'SineInOut';
		/** 缓和系数 **/
		public static const SineOutIn:String = 'SineOutIn';
		/** 缓和系数 **/
		public static const Linear:String = 'Linear';
		
		/** 缓动周期 单位ms **/
		
		private var target:Object;
		private var diration:Number;
		private var param:Object;
		
		private var disposed:Boolean = false;
		private var old:Object;
		private var correction:Object;
		private var r90:Number = Math.PI * 0.5;
		private var f:Number;
		private var offsetPercent:Number;
		private var rate:uint;
		private var timeNode:TimeNode;
		
		/**
		 * 简单过渡
		 * @param	target
		 * @param	diration
		 * @param	param
		 * @param   rate  缓动周期 单位ms
		 */
		public function Tween(target:Object,diration:uint,param:Object,rate:uint=33) 
		{
			this.target = target;
			this.diration = diration;
			this.param = param;
			
			if (rate ==0 ) throw new Error('rate 最小定时周期怎么可以是0');
			
			var n:int = diration / rate;
			var dx:int = diration - n * rate;
			
			offsetPercent = (dx / n) / dx;
			
			f = 1 / n;
			
			old = { };
			correction = { };
			
			
			/**
			 * 如果时间节点没有初始化管理器。就默认给一个
			 */
			if (TimeNode.timerManager == null)
			{
				TimeNode.timerManager = new TimerManager();
			}
			
			timeNode = new TimeNode(rate, param['delay']?param['delay']:0, n);
			timeNode.onTime = onTime;
			timeNode.onTimeComplete = onTimeComplete;
		}
		
		////////////////////////////////////////////////////////
		// method
		////////////////////////////////////////////////////////
		
		/** 立即完成过渡 **/
		public function complete():void
		{
			onTimeComplete(null);
		}
		
		private function dispose():void
		{
			if (disposed) return;
			disposed = true;
			
			timeNode.death = true;
			timeNode.onTime = null;
			timeNode.onTimeComplete = null;
			timeNode = null;
			
			target = null;
			param = null;
		}
		
		/**
		 * 计算当前时刻过渡值
		 * @param	easeType缓动类型
		 * @param	start	开始时刻值
		 * @param	end     结束时刻值
		 * @param	n		第几个时间分片
		 * @return
		 */
		private function calcAdd(easeType:String,start:Number,end:Number,n:int):Number
		{
			easeType = easeType || Linear;
			
			var p:Number=0;
			
			switch (easeType) 
			{
				case Linear:
					p = f;
				break;
				case SineInOut:
					p = Math.sin(n*f*r90) - (n>1?Math.sin((n-1)*f*r90):0);
				break;
				case SineOutIn:
					p = (Math.sin(n*f*r90+r90*3)+1)- (n>1?(Math.sin((n-1)*f*r90+r90*3)+1):0);
				break;
				default:
					p = f;
			}
			
			return (end - start) * p;
		}
		
		////////////////////////////////////////////////////////
		// evt handlers
		////////////////////////////////////////////////////////
		
		private function onTime(t:TimeNode):void 
		{
			var name:String;
			
			for (name in param) 
			{
				if (target.hasOwnProperty(name) == false) 
				{
					continue;
				}
				
				var property:*= param[name];
				
				if (property == target[name]) 
				{
					delete param[name];
					continue;
				}
				
				if (property is Number || property is int || property is uint)
				{
					if (old.hasOwnProperty(name)==false)
					{
						old[name] = target[name];
						correction[name] = param[name] - target[name];
					}
					
					var dt:Number = calcAdd(param['ease'], old[name], property, t.currentCount);
					correction[name] -= dt;
					target[name] += dt;
				}
			}
		}
		
		private function onTimeComplete(t:TimeNode):void 
		{
			for (var name:String in param) 
			{
				if (target.hasOwnProperty(name))
				{
					if (target[name] is Boolean)
					{
						target[name] = param[name];
					}
					else (target[name] is Number || target[name] is int || target[name] is uint)
					{
						//最后加上一个修正值
						target[name] += correction[name];
					}
				}
			}
			
			if (param['onComplete']) param['onComplete'](); 
			
			dispose();
		}
	}
}